package com.gitee.wsl.security.hash

import com.gitee.wsl.base.Objects.hash
import com.gitee.wsl.ext.array.readInt
import com.gitee.wsl.ext.array.readLong
import com.gitee.wsl.ext.base.Arrays.copyOfRange
import com.gitee.wsl.ext.bitcode.reverseBytes

/**
 * Google发布的Hash计算算法：CityHash64 与 CityHash128。<br></br>
 * 它们分别根据字串计算 64 和 128 位的散列值。这些算法不适用于加密，但适合用在散列表等处。
 *
 *
 *
 * 代码来自：https://github.com/rolandhe/string-tools<br></br>
 * 原始算法：https://github.com/google/cityhash
 *
 * @author hexiufeng
 * @since 5.2.5
 */
object CityHash {
    // Some primes between 2^63 and 2^64 for various uses.
    private const val k0 = -0x3c5a37a36834ced9L
    private const val k1 = -0x4b6d499041670d8dL
    private const val k2 = -0x651e95c4d06fbfb1L
    private const val kMul = -0x622015f714c7d297L

    // Magic numbers for 32-bit hashing.  Copied from Murmur3.
    private const val c1 = -0x3361d2af
    private const val c2 = 0x1b873593


    /**
     * 计算32位City Hash值
     *
     * @param data 数据
     * @return hash值
     */
    fun hash32(data: ByteArray): Int {
        val len = data.size
        if (len <= 24) {
            return if (len <= 12) (if (len <= 4) hash32Len0to4(data) else hash32Len5to12(data)) else hash32Len13to24(
                data
            )
        }

        // len > 24
        var h = len
        var g = c1 * len
        var f = g
        var a0 = rotate32(fetch32(data, len - 4) * c1, 17) * c2
        var a1 = rotate32(fetch32(data, len - 8) * c1, 17) * c2
        var a2 = rotate32(fetch32(data, len - 16) * c1, 17) * c2
        var a3 = rotate32(fetch32(data, len - 12) * c1, 17) * c2
        var a4 = rotate32(fetch32(data, len - 20) * c1, 17) * c2
        h = h xor a0
        h = rotate32(h, 19)
        h = h * 5 + -0x19ab949c
        h = h xor a2
        h = rotate32(h, 19)
        h = h * 5 + -0x19ab949c
        g = g xor a1
        g = rotate32(g, 19)
        g = g * 5 + -0x19ab949c
        g = g xor a3
        g = rotate32(g, 19)
        g = g * 5 + -0x19ab949c
        f += a4
        f = rotate32(f, 19)
        f = f * 5 + -0x19ab949c
        var iters = (len - 1) / 20

        var pos = 0
        do {
            a0 = rotate32(fetch32(data, pos) * c1, 17) * c2
            a1 = fetch32(data, pos + 4)
            a2 = rotate32(fetch32(data, pos + 8) * c1, 17) * c2
            a3 = rotate32(fetch32(data, pos + 12) * c1, 17) * c2
            a4 = fetch32(data, pos + 16)
            h = h xor a0
            h = rotate32(h, 18)
            h = h * 5 + -0x19ab949c
            f += a1
            f = rotate32(f, 19)
            f = f * c1
            g += a2
            g = rotate32(g, 18)
            g = g * 5 + -0x19ab949c
            h = h xor a3 + a1
            h = rotate32(h, 19)
            h = h * 5 + -0x19ab949c
            g = g xor a4
            g = g.reverseBytes() * 5
            h += a4 * 5
            h = h.reverseBytes()
            f += a0
            val swapValue = f
            f = g
            g = h
            h = swapValue

            pos += 20
        } while (--iters != 0)

        g = rotate32(g, 11) * c1
        g = rotate32(g, 17) * c1
        f = rotate32(f, 11) * c1
        f = rotate32(f, 17) * c1
        h = rotate32(h + g, 19)
        h = h * 5 + -0x19ab949c
        h = rotate32(h, 17) * c1
        h = rotate32(h + f, 19)
        h = h * 5 + -0x19ab949c
        h = rotate32(h, 17) * c1
        return h
    }

    /**
     * 计算64位City Hash值
     *
     * @param data 数据
     * @return hash值
     */
    fun hash64(data: ByteArray): Long {
        var len = data.size
        if (len <= 32) {
            if (len <= 16) {
                return hashLen0to16(data)
            } else {
                return hashLen17to32(data)
            }
        } else if (len <= 64) {
            return hashLen33to64(data)
        }

        // For strings over 64 bytes we hash the end first, and then as we
        // loop we keep 56 bytes of state: v, w, x, y, and z.
        var x = fetch64(data, len - 40)
        var y = fetch64(data, len - 16) + fetch64(data, len - 56)
        var z = hashLen16(fetch64(data, len - 48) + len, fetch64(data, len - 24))
        var v = weakHashLen32WithSeeds(data, len - 64, len.toLong(), z)
        var w = weakHashLen32WithSeeds(data, len - 32, y + k1, x)
        x = x * k1 + fetch64(data, 0)

        // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
        len = (len - 1) and 63.inv()
        var pos = 0
        do {
            x = rotate64(x + y + v.lowValue + fetch64(data, pos + 8), 37) * k1
            y = rotate64(y + v.highValue + fetch64(data, pos + 48), 42) * k1
            x = x xor w.highValue
            y += v.lowValue + fetch64(data, pos + 40)
            z = rotate64(z + w.lowValue, 33) * k1
            v = weakHashLen32WithSeeds(data, pos, v.highValue * k1, x + w.lowValue)
            w = weakHashLen32WithSeeds(data, pos + 32, z + w.highValue, y + fetch64(data, pos + 16))
            // swap z,x value
            val swapValue = x
            x = z
            z = swapValue
            pos += 64
            len -= 64
        } while (len != 0)
        return hashLen16(
            hashLen16(v.lowValue, w.lowValue) + shiftMix(y) * k1 + z,
            hashLen16(v.highValue, w.highValue) + x
        )
    }

    /**
     * 计算64位City Hash值
     *
     * @param data  数据
     * @param seed0 种子1
     * @param seed1 种子2
     * @return hash值
     */
    fun hash64(data: ByteArray, seed0: Long, seed1: Long): Long {
        return hashLen16(hash64(data) - seed0, seed1)
    }

    /**
     * 计算64位City Hash值，种子1使用默认的[.k2]
     *
     * @param data 数据
     * @param seed 种子2
     * @return hash值
     */
    fun hash64(data: ByteArray, seed: Long): Long {
        return hash64(data, k2, seed)
    }

    /**
     * 计算128位City Hash值
     *
     * @param data 数据
     * @return hash值
     */
    fun hash128(data: ByteArray): Number128 {
        val len = data.size
        return if (len >= 16) hash128(
            data, 16,
            Number128(fetch64(data, 0), fetch64(data, 8) + k0)
        ) else hash128(data, 0, Number128(k0, k1))
    }

    /**
     * 计算128位City Hash值
     *
     * @param data 数据
     * @param seed 种子
     * @return hash值
     */
    fun hash128(data: ByteArray, seed: Number128): Number128 {
        return hash128(data, 0, seed)
    }

    //------------------------------------------------------------------------------------------------------- Private method start
    private fun hash128(byteArray: ByteArray, start: Int, seed: Number128): Number128 {
        var len = byteArray.size - start

        if (len < 128) {
            return cityMurmur(copyOfRange(byteArray, start, byteArray.size), seed)
        }

        // We expect len >= 128 to be the common case.  Keep 56 bytes of state:
        // v, w, x, y, and z.
        var v = Number128(0L, 0L)
        var w = Number128(0L, 0L)
        var x = seed.lowValue
        var y = seed.highValue
        var z = len * k1
        v.lowValue = rotate64(y xor k1, 49) * k1 + fetch64(byteArray, start)
        v.highValue = rotate64(v.lowValue, 42) * k1 + fetch64(byteArray, start + 8)
        w.lowValue = rotate64(y + z, 35) * k1 + x
        w.highValue = rotate64(x + fetch64(byteArray, start + 88), 53) * k1

        // This is the same inner loop as CityHash64(), manually unrolled.
        var pos = start
        do {
            x = rotate64(x + y + v.lowValue + fetch64(byteArray, pos + 8), 37) * k1
            y = rotate64(y + v.highValue + fetch64(byteArray, pos + 48), 42) * k1
            x = x xor w.highValue
            y += v.lowValue + fetch64(byteArray, pos + 40)
            z = rotate64(z + w.lowValue, 33) * k1
            v = weakHashLen32WithSeeds(byteArray, pos, v.highValue * k1, x + w.lowValue)
            w = weakHashLen32WithSeeds(byteArray, pos + 32, z + w.highValue, y + fetch64(byteArray, pos + 16))

            var swapValue = x
            x = z
            z = swapValue
            pos += 64
            x = rotate64(x + y + v.lowValue + fetch64(byteArray, pos + 8), 37) * k1
            y = rotate64(y + v.highValue + fetch64(byteArray, pos + 48), 42) * k1
            x = x xor w.highValue
            y += v.lowValue + fetch64(byteArray, pos + 40)
            z = rotate64(z + w.lowValue, 33) * k1
            v = weakHashLen32WithSeeds(byteArray, pos, v.highValue * k1, x + w.lowValue)
            w = weakHashLen32WithSeeds(byteArray, pos + 32, z + w.highValue, y + fetch64(byteArray, pos + 16))
            swapValue = x
            x = z
            z = swapValue
            pos += 64
            len -= 128
        } while (len >= 128)
        x += rotate64(v.lowValue + z, 49) * k0
        y = y * k0 + rotate64(w.highValue, 37)
        z = z * k0 + rotate64(w.lowValue, 27)
        w.lowValue = w.lowValue * 9
        v.lowValue = v.lowValue * k0

        // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
        var tail_done = 0
        while (tail_done < len) {
            tail_done += 32
            y = rotate64(x + y, 42) * k0 + v.highValue
            w.lowValue = w.lowValue + fetch64(byteArray, pos + len - tail_done + 16)
            x = x * k0 + w.lowValue
            z += w.highValue + fetch64(byteArray, pos + len - tail_done)
            w.highValue = w.highValue + v.lowValue
            v = weakHashLen32WithSeeds(byteArray, pos + len - tail_done, v.lowValue + z, v.highValue)
            v.lowValue = v.lowValue * k0
        }
        // At this point our 56 bytes of state should contain more than
        // enough information for a strong 128-bit hash.  We use two
        // different 56-byte-to-8-byte hashes to get a 16-byte final result.
        x = hashLen16(x, v.lowValue)
        y = hashLen16(y + z, w.lowValue)
        return Number128(
            hashLen16(x + v.highValue, w.highValue) + y,
            hashLen16(x + w.highValue, y + v.highValue)
        )
    }

    private fun hash32Len0to4(byteArray: ByteArray): Int {
        var b = 0
        var c = 9
        val len = byteArray.size
        for (v in byteArray) {
            b = b * c1 + v
            c = c xor b
        }
        return fmix(mur(b, mur(len, c)))
    }

    private fun hash32Len5to12(byteArray: ByteArray): Int {
        val len = byteArray.size
        var a = len
        var b = len * 5
        var c = 9
        val d = b
        a += fetch32(byteArray, 0)
        b += fetch32(byteArray, len - 4)
        c += fetch32(byteArray, ((len ushr 1) and 4))
        return fmix(mur(c, mur(b, mur(a, d))))
    }

    private fun hash32Len13to24(byteArray: ByteArray): Int {
        val len = byteArray.size
        val a = fetch32(byteArray, (len ushr 1) - 4)
        val b = fetch32(byteArray, 4)
        val c = fetch32(byteArray, len - 8)
        val d = fetch32(byteArray, (len ushr 1))
        val e = fetch32(byteArray, 0)
        val f = fetch32(byteArray, len - 4)
        val h = len

        return fmix(mur(f, mur(e, mur(d, mur(c, mur(b, mur(a, h)))))))
    }

    private fun hashLen0to16(byteArray: ByteArray): Long {
        val len = byteArray.size
        if (len >= 8) {
            val mul = k2 + len * 2L
            val a = fetch64(byteArray, 0) + k2
            val b = fetch64(byteArray, len - 8)
            val c = rotate64(b, 37) * mul + a
            val d = (rotate64(a, 25) + b) * mul
            return hashLen16(c, d, mul)
        }
        if (len >= 4) {
            val mul = k2 + len * 2
            val a = fetch32(byteArray, 0).toLong() and 0xffffffffL
            return hashLen16(len + (a shl 3), fetch32(byteArray, len - 4).toLong() and 0xffffffffL, mul)
        }
        if (len > 0) {
            val a = byteArray[0].toInt() and 0xff
            val b = byteArray[len ushr 1].toInt() and 0xff
            val c = byteArray[len - 1].toInt() and 0xff
            val y = a + (b shl 8)
            val z = len + (c shl 2)
            return shiftMix(y * k2 xor z * k0) * k2
        }
        return k2
    }

    // This probably works well for 16-byte strings as well, but it may be overkill in that case.
    private fun hashLen17to32(byteArray: ByteArray): Long {
        val len = byteArray.size
        val mul = k2 + len * 2L
        val a = fetch64(byteArray, 0) * k1
        val b = fetch64(byteArray, 8)
        val c = fetch64(byteArray, len - 8) * mul
        val d = fetch64(byteArray, len - 16) * k2
        return hashLen16(
            rotate64(a + b, 43) + rotate64(c, 30) + d,
            a + rotate64(b + k2, 18) + c, mul
        )
    }

    private fun hashLen33to64(byteArray: ByteArray): Long {
        val len = byteArray.size
        val mul = k2 + len * 2L
        var a = fetch64(byteArray, 0) * k2
        var b = fetch64(byteArray, 8)
        val c = fetch64(byteArray, len - 24)
        val d = fetch64(byteArray, len - 32)
        val e = fetch64(byteArray, 16) * k2
        val f = fetch64(byteArray, 24) * 9
        val g = fetch64(byteArray, len - 8)
        val h = fetch64(byteArray, len - 16) * mul
        val u = rotate64(a + g, 43) + (rotate64(b, 30) + c) * 9
        val v = ((a + g) xor d) + f + 1
        val w: Long = ((u + v) * mul).reverseBytes() + h
        val x = rotate64(e + f, 42) + c
        val y: Long = (((v + w) * mul).reverseBytes() + g) * mul
        val z = e + f + c
        a = ((x + z) * mul + y).reverseBytes() + b
        b = shiftMix((z + a) * mul + d + h) * mul
        return b + x
    }

    private fun fetch64(byteArray: ByteArray, start: Int): Long {
        return byteArray.readLong(start)
        //return ByteUtil.bytesToLong(byteArray, start, ByteUtil.CPU_ENDIAN)
    }

    private fun fetch32(byteArray: ByteArray, start: Int): Int {
        return byteArray.readInt(start)
        //return ByteUtil.bytesToInt(byteArray, start, ByteUtil.CPU_ENDIAN)
    }

    private fun rotate64(`val`: Long, shift: Int): Long {
        // Avoid shifting by 64: doing so yields an undefined result.
        return if (shift == 0) `val` else ((`val` ushr shift) or (`val` shl (64 - shift)))
    }

    private fun rotate32(`val`: Int, shift: Int): Int {
        // Avoid shifting by 32: doing so yields an undefined result.
        return if (shift == 0) `val` else ((`val` ushr shift) or (`val` shl (32 - shift)))
    }

    private fun hashLen16(u: Long, v: Long, mul: Long): Long {
        // Murmur-inspired hashing.
        var a = (u xor v) * mul
        a = a xor (a ushr 47)
        var b = (v xor a) * mul
        b = b xor (b ushr 47)
        b *= mul
        return b
    }

    private fun hashLen16(u: Long, v: Long): Long {
        return hash128to64(Number128(u, v))
    }

    private fun hash128to64(number128: Number128): Long {
        // Murmur-inspired hashing.
        var a = (number128.lowValue xor number128.highValue) * kMul
        a = a xor (a ushr 47)
        var b = (number128.highValue xor a) * kMul
        b = b xor (b ushr 47)
        b *= kMul
        return b
    }

    private fun shiftMix(`val`: Long): Long {
        return `val` xor (`val` ushr 47)
    }

    private fun fmix(h: Int): Int {
        var h = h
        h = h xor (h ushr 16)
        h *= -0x7a143595
        h = h xor (h ushr 13)
        h *= -0x3d4d51cb
        h = h xor (h ushr 16)
        return h
    }

    private fun mur(a: Int, h: Int): Int {
        // Helper from Murmur3 for combining two 32-bit values.
        var a = a
        var h = h
        a *= c1
        a = rotate32(a, 17)
        a *= c2
        h = h xor a
        h = rotate32(h, 19)
        return h * 5 + -0x19ab949c
    }

    private fun weakHashLen32WithSeeds(
        w: Long, x: Long, y: Long, z: Long, a: Long, b: Long,
    ): Number128 {
        var a = a
        var b = b
        a += w
        b = rotate64(b + a + z, 21)
        val c = a
        a += x
        a += y
        b += rotate64(a, 44)
        return Number128(a + z, b + c)
    }

    // Return a 16-byte hash for s[0] ... s[31], a, and b.  Quick and dirty.
    private fun weakHashLen32WithSeeds(
        byteArray: ByteArray, start: Int, a: Long, b: Long,
    ): Number128 {
        return weakHashLen32WithSeeds(
            fetch64(byteArray, start),
            fetch64(byteArray, start + 8),
            fetch64(byteArray, start + 16),
            fetch64(byteArray, start + 24),
            a,
            b
        )
    }

    private fun cityMurmur(byteArray: ByteArray, seed: Number128): Number128 {
        val len = byteArray.size
        var a = seed.lowValue
        var b = seed.highValue
        var c: Long
        var d: Long
        var l = len - 16
        if (l <= 0) {  // len <= 16
            a = shiftMix(a * k1) * k1
            c = b * k1 + hashLen0to16(byteArray)
            d = shiftMix(a + (if (len >= 8) fetch64(byteArray, 0) else c))
        } else {  // len > 16
            c = hashLen16(fetch64(byteArray, len - 8) + k1, a)
            d = hashLen16(b + len, c + fetch64(byteArray, len - 16))
            a += d
            var pos = 0
            do {
                a = a xor shiftMix(fetch64(byteArray, pos) * k1) * k1
                a *= k1
                b = b xor a
                c = c xor shiftMix(fetch64(byteArray, pos + 8) * k1) * k1
                c *= k1
                d = d xor c
                pos += 16
                l -= 16
            } while (l > 0)
        }
        a = hashLen16(a, c)
        b = hashLen16(d, b)
        return Number128(a xor b, hashLen16(b, a))
    } //------------------------------------------------------------------------------------------------------- Private method end
}

/**
 * 128位数字表示，分高位和低位
 *
 * @author hexiufeng
 * @since 5.2.5
 */
class Number128(
    /**
     * 设置低位值
     *
     * @param lowValue 低位值
     */
    var lowValue: Long,
    /**
     * 设置高位值
     *
     * @param hiValue 高位值
     */
    var highValue: Long,
) : Number() {

    val longArray: LongArray
        /**
         * 获取高低位数组，long[0]：低位，long[1]：高位
         *
         * @return 高低位数组，long[0]：低位，long[1]：高位
         */
        get() = longArrayOf(lowValue, highValue)


    override fun equals(o: Any?): Boolean {
        if (this === o) return true
        if (o == null || this::class != o::class) {
            return false
        }
        val number128 = o as Number128
        return lowValue == number128.lowValue && highValue == number128.highValue
    }

    override fun hashCode(): Int {
        return hash(lowValue, highValue)
    }

    override fun toDouble(): Double = toLong().toDouble()

    override fun toFloat(): Float = toLong().toFloat()

    override fun toLong(): Long {
        return this.lowValue
    }

    override fun toInt(): Int = toLong().toInt()

    override fun toShort(): Short = toLong().toShort()
    override fun toByte(): Byte = toLong().toByte()

}


